
<!DOCTYPE html>
<html lang="">


<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
  <meta name="theme-color" content="#202020">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <script src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" async></script>
  
  
    <meta name="keywords" content>
  

  
    <meta name="description" content="Gin框架学习">
  
  
  
  <link rel="icon" type="image/x-icon" href="/images/footer-logo.png">
  
  <title>Gin框架学习 [ 51AIOps 专注于运维自动化  微信： kaipython ]</title>
  
    <!-- stylesheets list from config.yml -->
    
      <link rel="stylesheet" href="//cdn.bootcss.com/pure/1.0.0/pure-min.css">
    
      <link rel="stylesheet" href="/css/xoxo.css">
    
  
</head>


<body>
  <div class="nav-container">
    <nav class="home-menu pure-menu pure-menu-horizontal">
  <a class="pure-menu-heading" href="/">
    
    <span class="title" style="text-transform:none">51AIOps 专注于运维自动化  微信： kaipython</span>
  </a>

  <ul class="pure-menu-list clearfix">
      
          
            
              <li class="pure-menu-item"><a href="/" class="pure-menu-link">首页</a></li>
            
          
      
  </ul>
   
</nav>

  </div>

  <div class="container" id="content-outer">
    <div class="inner" id="content-inner" style='margin-left:-68px!important'>
      <div class="post-container">
  <article class="post" id="post">
    <header class="post-header text-center">
      <h1 class="title">
        Gin框架学习
      </h1>
      <span>
        
        <time class="time" datetime="2020-05-03T16:00:00.000Z">
        2020-05-04
      </time>
        
      </span>
      <span class="slash">/</span>
      <span class="post-meta">
      <span class="post-tags">
        
      </span>
    </span>
      <span class="slash">/</span>
      <span class="read">
      <span id="busuanzi_value_page_pv"></span> 点击
    </span>
      <span class="slash">/</span>
    </header>

    <div class="post-content">
      <p><code>Gin</code>是一个用Go语言编写的web框架。它是一个类似于<code>martini</code>但拥有更好性能的API框架, 由于使用了<code>httprouter</code>，速度提高了近40倍。 如果你是性能和高效的追求者, 你会爱上<code>Gin</code>。</p>
<h2 id="Gin框架介绍"><a href="#Gin框架介绍" class="headerlink" title="Gin框架介绍"></a>Gin框架介绍</h2><p>Go世界里最流行的Web框架，<a href="https://github.com/gin-gonic/gin" target="_blank" rel="noopener">Github</a>上有<code>32K+</code>star。 基于<a href="https://github.com/julienschmidt/httprouter" target="_blank" rel="noopener">httprouter</a>开发的Web框架。 <a href="https://gin-gonic.com/zh-cn/docs/" target="_blank" rel="noopener">中文文档</a>齐全，简单易用的轻量级框架。</p>
<h2 id="Gin框架安装与使用"><a href="#Gin框架安装与使用" class="headerlink" title="Gin框架安装与使用"></a>Gin框架安装与使用</h2><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>下载并安装<code>Gin</code>:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">go get -u github.com/gin-gonic/gin</span><br></pre></td></tr></table></figure>

<h3 id="第一个Gin示例："><a href="#第一个Gin示例：" class="headerlink" title="第一个Gin示例："></a>第一个Gin示例：</h3><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	<span class="string">"github.com/gin-gonic/gin"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">// 创建一个默认的路由引擎</span></span><br><span class="line">	r := gin.Default()</span><br><span class="line">	<span class="comment">// GET：请求方式；/hello：请求的路径</span></span><br><span class="line">	<span class="comment">// 当客户端以GET方法请求/hello路径时，会执行后面的匿名函数</span></span><br><span class="line">	r.GET(<span class="string">"/hello"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="comment">// c.JSON：返回JSON格式的数据</span></span><br><span class="line">		c.JSON(<span class="number">200</span>, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>: <span class="string">"Hello world!"</span>,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line">	<span class="comment">// 启动HTTP服务，默认在0.0.0.0:8080启动服务</span></span><br><span class="line">	r.Run()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>将上面的代码保存并编译执行，然后使用浏览器打开<code>127.0.0.1:8080/hello</code>就能看到一串JSON字符串。</p>
<h2 id="RESTful-API"><a href="#RESTful-API" class="headerlink" title="RESTful API"></a>RESTful API</h2><p>REST与技术无关，代表的是一种软件架构风格，REST是Representational State Transfer的简称，中文翻译为“表征状态转移”或“表现层状态转化”。</p>
<p>推荐阅读<a href="http://www.ruanyifeng.com/blog/2011/09/restful.html" target="_blank" rel="noopener">阮一峰 理解RESTful架构</a></p>
<p>简单来说，REST的含义就是客户端与Web服务器之间进行交互的时候，使用HTTP协议中的4个请求方法代表不同的动作。</p>
<ul>
<li><code>GET</code>用来获取资源</li>
<li><code>POST</code>用来新建资源</li>
<li><code>PUT</code>用来更新资源</li>
<li><code>DELETE</code>用来删除资源。</li>
</ul>
<p>只要API程序遵循了REST风格，那就可以称其为RESTful API。目前在前后端分离的架构中，前后端基本都是通过RESTful API来进行交互。</p>
<p>例如，我们现在要编写一个管理书籍的系统，我们可以查询对一本书进行查询、创建、更新和删除等操作，我们在编写程序的时候就要设计客户端浏览器与我们Web服务端交互的方式和路径。按照经验我们通常会设计成如下模式：</p>
<table>
<thead>
<tr>
<th align="center">请求方法</th>
<th align="center">URL</th>
<th align="center">含义</th>
</tr>
</thead>
<tbody><tr>
<td align="center">GET</td>
<td align="center">/book</td>
<td align="center">查询书籍信息</td>
</tr>
<tr>
<td align="center">POST</td>
<td align="center">/create_book</td>
<td align="center">创建书籍记录</td>
</tr>
<tr>
<td align="center">POST</td>
<td align="center">/update_book</td>
<td align="center">更新书籍信息</td>
</tr>
<tr>
<td align="center">POST</td>
<td align="center">/delete_book</td>
<td align="center">删除书籍信息</td>
</tr>
</tbody></table>
<p>同样的需求我们按照RESTful API设计如下：</p>
<table>
<thead>
<tr>
<th align="center">请求方法</th>
<th align="center">URL</th>
<th align="center">含义</th>
</tr>
</thead>
<tbody><tr>
<td align="center">GET</td>
<td align="center">/book</td>
<td align="center">查询书籍信息</td>
</tr>
<tr>
<td align="center">POST</td>
<td align="center">/book</td>
<td align="center">创建书籍记录</td>
</tr>
<tr>
<td align="center">PUT</td>
<td align="center">/book</td>
<td align="center">更新书籍信息</td>
</tr>
<tr>
<td align="center">DELETE</td>
<td align="center">/book</td>
<td align="center">删除书籍信息</td>
</tr>
</tbody></table>
<p>Gin框架支持开发RESTful API的开发。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	r := gin.Default()</span><br><span class="line">	r.GET(<span class="string">"/book"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.JSON(<span class="number">200</span>, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>: <span class="string">"GET"</span>,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	r.POST(<span class="string">"/book"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.JSON(<span class="number">200</span>, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>: <span class="string">"POST"</span>,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	r.PUT(<span class="string">"/book"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.JSON(<span class="number">200</span>, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>: <span class="string">"PUT"</span>,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	r.DELETE(<span class="string">"/book"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.JSON(<span class="number">200</span>, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>: <span class="string">"DELETE"</span>,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>开发RESTful API的时候我们通常使用<a href="https://www.getpostman.com/" target="_blank" rel="noopener">Postman</a>来作为客户端的测试工具。</p>
<h2 id="Gin渲染"><a href="#Gin渲染" class="headerlink" title="Gin渲染"></a>Gin渲染</h2><h3 id="HTML渲染"><a href="#HTML渲染" class="headerlink" title="HTML渲染"></a>HTML渲染</h3><p>我们首先定义一个存放模板文件的<code>templates</code>文件夹，然后在其内部按照业务分别定义一个<code>posts</code>文件夹和一个<code>users</code>文件夹。 <code>posts/index.html</code>文件的内容如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#123;define &quot;posts/index.html&quot;&#125;&#125;</span><br><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html lang=&quot;en&quot;&gt;</span><br><span class="line"></span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;meta charset=&quot;UTF-8&quot;&gt;</span><br><span class="line">    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;</span><br><span class="line">    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;ie=edge&quot;&gt;</span><br><span class="line">    &lt;title&gt;posts/index&lt;/title&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">    &#123;&#123;.title&#125;&#125;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br><span class="line">&#123;&#123;end&#125;&#125;</span><br></pre></td></tr></table></figure>

<p><code>users/index.html</code>文件的内容如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&#123;&#123;define &quot;users/index.html&quot;&#125;&#125;</span><br><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html lang=&quot;en&quot;&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;meta charset=&quot;UTF-8&quot;&gt;</span><br><span class="line">    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;</span><br><span class="line">    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;ie=edge&quot;&gt;</span><br><span class="line">    &lt;title&gt;users/index&lt;/title&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">    &#123;&#123;.title&#125;&#125;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br><span class="line">&#123;&#123;end&#125;&#125;</span><br></pre></td></tr></table></figure>

<p>Gin框架中使用<code>LoadHTMLGlob()</code>或者<code>LoadHTMLFiles()</code>方法进行HTML模板渲染。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	r := gin.Default()</span><br><span class="line">	r.LoadHTMLGlob(<span class="string">"templates/**/*"</span>)</span><br><span class="line">	<span class="comment">//r.LoadHTMLFiles("templates/posts/index.html", "templates/users/index.html")</span></span><br><span class="line">	r.GET(<span class="string">"/posts/index"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.HTML(http.StatusOK, <span class="string">"posts/index.html"</span>, gin.H&#123;</span><br><span class="line">			<span class="string">"title"</span>: <span class="string">"posts/index"</span>,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	r.GET(<span class="string">"users/index"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.HTML(http.StatusOK, <span class="string">"users/index.html"</span>, gin.H&#123;</span><br><span class="line">			<span class="string">"title"</span>: <span class="string">"users/index"</span>,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	r.Run(<span class="string">":8080"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="自定义模板函数"><a href="#自定义模板函数" class="headerlink" title="自定义模板函数"></a>自定义模板函数</h3><p>定义一个不转义相应内容的<code>safe</code>模板函数如下：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	router := gin.Default()</span><br><span class="line">	router.SetFuncMap(template.FuncMap&#123;</span><br><span class="line">		<span class="string">"safe"</span>: <span class="function"><span class="keyword">func</span><span class="params">(str <span class="keyword">string</span>)</span> <span class="title">template</span>.<span class="title">HTML</span></span>&#123;</span><br><span class="line">			<span class="keyword">return</span> template.HTML(str)</span><br><span class="line">		&#125;,</span><br><span class="line">	&#125;)</span><br><span class="line">	router.LoadHTMLFiles(<span class="string">"./index.tmpl"</span>)</span><br><span class="line"></span><br><span class="line">	router.GET(<span class="string">"/index"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.HTML(http.StatusOK, <span class="string">"index.tmpl"</span>, <span class="string">"&lt;a href='https://liwenzhou.com'&gt;李文周的博客&lt;/a&gt;"</span>)</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	router.Run(<span class="string">":8080"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在<code>index.tmpl</code>中使用定义好的<code>safe</code>模板函数：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html lang=&quot;zh-CN&quot;&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;title&gt;修改模板引擎的标识符&lt;/title&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">&lt;div&gt;&#123;&#123; . | safe &#125;&#125;&lt;/div&gt;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure>

<h3 id="静态文件处理"><a href="#静态文件处理" class="headerlink" title="静态文件处理"></a>静态文件处理</h3><p>当我们渲染的HTML文件中引用了静态文件时，我们只需要按照以下方式在渲染页面前调用<code>gin.Static</code>方法即可。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	r := gin.Default()</span><br><span class="line">	r.Static(<span class="string">"/static"</span>, <span class="string">"./static"</span>)</span><br><span class="line">	r.LoadHTMLGlob(<span class="string">"templates/**/*"</span>)</span><br><span class="line">   <span class="comment">// ...</span></span><br><span class="line">	r.Run(<span class="string">":8080"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="使用模板继承"><a href="#使用模板继承" class="headerlink" title="使用模板继承"></a>使用模板继承</h3><p>Gin框架默认都是使用单模板，如果需要使用<code>block template</code>功能，可以通过<code>&quot;github.com/gin-contrib/multitemplate&quot;</code>库实现，具体示例如下：</p>
<p>首先，假设我们项目目录下的templates文件夹下有以下模板文件，其中<code>home.tmpl</code>和<code>index.tmpl</code>继承了<code>base.tmpl</code>：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">templates</span><br><span class="line">├── includes</span><br><span class="line">│   ├── home.tmpl</span><br><span class="line">│   └── index.tmpl</span><br><span class="line">├── layouts</span><br><span class="line">│   └── base.tmpl</span><br><span class="line">└── scripts.tmpl</span><br></pre></td></tr></table></figure>

<p>然后我们定义一个<code>loadTemplates</code>函数如下：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">loadTemplates</span><span class="params">(templatesDir <span class="keyword">string</span>)</span> <span class="title">multitemplate</span>.<span class="title">Renderer</span></span> &#123;</span><br><span class="line">	r := multitemplate.NewRenderer()</span><br><span class="line">	layouts, err := filepath.Glob(templatesDir + <span class="string">"/layouts/*.tmpl"</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err.Error())</span><br><span class="line">	&#125;</span><br><span class="line">	includes, err := filepath.Glob(templatesDir + <span class="string">"/includes/*.tmpl"</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err.Error())</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// 为layouts/和includes/目录生成 templates map</span></span><br><span class="line">	<span class="keyword">for</span> _, include := <span class="keyword">range</span> includes &#123;</span><br><span class="line">		layoutCopy := <span class="built_in">make</span>([]<span class="keyword">string</span>, <span class="built_in">len</span>(layouts))</span><br><span class="line">		<span class="built_in">copy</span>(layoutCopy, layouts)</span><br><span class="line">		files := <span class="built_in">append</span>(layoutCopy, include)</span><br><span class="line">		r.AddFromFiles(filepath.Base(include), files...)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> r</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们在<code>main</code>函数中</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">indexFunc</span><span class="params">(c *gin.Context)</span></span>&#123;</span><br><span class="line">	c.HTML(http.StatusOK, <span class="string">"index.tmpl"</span>, <span class="literal">nil</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">homeFunc</span><span class="params">(c *gin.Context)</span></span>&#123;</span><br><span class="line">	c.HTML(http.StatusOK, <span class="string">"home.tmpl"</span>, <span class="literal">nil</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">	r := gin.Default()</span><br><span class="line">	r.HTMLRender = loadTemplates(<span class="string">"./templates"</span>)</span><br><span class="line">	r.GET(<span class="string">"/index"</span>, indexFunc)</span><br><span class="line">	r.GET(<span class="string">"/home"</span>, homeFunc)</span><br><span class="line">	r.Run()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="补充文件路径处理"><a href="#补充文件路径处理" class="headerlink" title="补充文件路径处理"></a>补充文件路径处理</h3><p>关于模板文件和静态文件的路径，我们需要根据公司/项目的要求进行设置。可以使用下面的函数获取当前执行程序的路径。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getCurrentPath</span><span class="params">()</span> <span class="title">string</span></span> &#123;</span><br><span class="line">	<span class="keyword">if</span> ex, err := os.Executable(); err == <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> filepath.Dir(ex)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="string">"./"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="JSON渲染"><a href="#JSON渲染" class="headerlink" title="JSON渲染"></a>JSON渲染</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	r := gin.Default()</span><br><span class="line"></span><br><span class="line">	<span class="comment">// gin.H 是map[string]interface&#123;&#125;的缩写</span></span><br><span class="line">	r.GET(<span class="string">"/someJSON"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="comment">// 方式一：自己拼接JSON</span></span><br><span class="line">		c.JSON(http.StatusOK, gin.H&#123;<span class="string">"message"</span>: <span class="string">"Hello world!"</span>&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line">	r.GET(<span class="string">"/moreJSON"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="comment">// 方法二：使用结构体</span></span><br><span class="line">		<span class="keyword">var</span> msg <span class="keyword">struct</span> &#123;</span><br><span class="line">			Name    <span class="keyword">string</span> <span class="string">`json:"user"`</span></span><br><span class="line">			Message <span class="keyword">string</span></span><br><span class="line">			Age     <span class="keyword">int</span></span><br><span class="line">		&#125;</span><br><span class="line">		msg.Name = <span class="string">"小王子"</span></span><br><span class="line">		msg.Message = <span class="string">"Hello world!"</span></span><br><span class="line">		msg.Age = <span class="number">18</span></span><br><span class="line">		c.JSON(http.StatusOK, msg)</span><br><span class="line">	&#125;)</span><br><span class="line">	r.Run(<span class="string">":8080"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="XML渲染"><a href="#XML渲染" class="headerlink" title="XML渲染"></a>XML渲染</h3><p>注意需要使用具名的结构体类型。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	r := gin.Default()</span><br><span class="line">	<span class="comment">// gin.H 是map[string]interface&#123;&#125;的缩写</span></span><br><span class="line">	r.GET(<span class="string">"/someXML"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="comment">// 方式一：自己拼接JSON</span></span><br><span class="line">		c.XML(http.StatusOK, gin.H&#123;<span class="string">"message"</span>: <span class="string">"Hello world!"</span>&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line">	r.GET(<span class="string">"/moreXML"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="comment">// 方法二：使用结构体</span></span><br><span class="line">		<span class="keyword">type</span> MessageRecord <span class="keyword">struct</span> &#123;</span><br><span class="line">			Name    <span class="keyword">string</span></span><br><span class="line">			Message <span class="keyword">string</span></span><br><span class="line">			Age     <span class="keyword">int</span></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">var</span> msg MessageRecord</span><br><span class="line">		msg.Name = <span class="string">"小王子"</span></span><br><span class="line">		msg.Message = <span class="string">"Hello world!"</span></span><br><span class="line">		msg.Age = <span class="number">18</span></span><br><span class="line">		c.XML(http.StatusOK, msg)</span><br><span class="line">	&#125;)</span><br><span class="line">	r.Run(<span class="string">":8080"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="YMAL渲染"><a href="#YMAL渲染" class="headerlink" title="YMAL渲染"></a>YMAL渲染</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">r.GET(<span class="string">"/someYAML"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">	c.YAML(http.StatusOK, gin.H&#123;<span class="string">"message"</span>: <span class="string">"ok"</span>, <span class="string">"status"</span>: http.StatusOK&#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="protobuf渲染"><a href="#protobuf渲染" class="headerlink" title="protobuf渲染"></a>protobuf渲染</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">r.GET(<span class="string">"/someProtoBuf"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">	reps := []<span class="keyword">int64</span>&#123;<span class="keyword">int64</span>(<span class="number">1</span>), <span class="keyword">int64</span>(<span class="number">2</span>)&#125;</span><br><span class="line">	label := <span class="string">"test"</span></span><br><span class="line">	<span class="comment">// protobuf 的具体定义写在 testdata/protoexample 文件中。</span></span><br><span class="line">	data := &amp;protoexample.Test&#123;</span><br><span class="line">		Label: &amp;label,</span><br><span class="line">		Reps:  reps,</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// 请注意，数据在响应中变为二进制数据</span></span><br><span class="line">	<span class="comment">// 将输出被 protoexample.Test protobuf 序列化了的数据</span></span><br><span class="line">	c.ProtoBuf(http.StatusOK, data)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h2 id="获取参数"><a href="#获取参数" class="headerlink" title="获取参数"></a>获取参数</h2><h3 id="获取querystring参数"><a href="#获取querystring参数" class="headerlink" title="获取querystring参数"></a>获取querystring参数</h3><p><code>querystring</code>指的是URL中<code>?</code>后面携带的参数，例如：<code>/user/search?username=小王子&amp;address=沙河</code>。 获取请求的querystring参数的方法如下：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">//Default返回一个默认的路由引擎</span></span><br><span class="line">	r := gin.Default()</span><br><span class="line">	r.GET(<span class="string">"/user/search"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		username := c.DefaultQuery(<span class="string">"username"</span>, <span class="string">"小王子"</span>)</span><br><span class="line">		<span class="comment">//username := c.Query("username")</span></span><br><span class="line">		address := c.Query(<span class="string">"address"</span>)</span><br><span class="line">		<span class="comment">//输出json结果给调用方</span></span><br><span class="line">		c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>:  <span class="string">"ok"</span>,</span><br><span class="line">			<span class="string">"username"</span>: username,</span><br><span class="line">			<span class="string">"address"</span>:  address,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line">	r.Run()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="获取form参数"><a href="#获取form参数" class="headerlink" title="获取form参数"></a>获取form参数</h3><p>请求的数据通过form表单来提交，例如向<code>/user/search</code>发送一个POST请求，获取请求数据的方式如下：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">//Default返回一个默认的路由引擎</span></span><br><span class="line">	r := gin.Default()</span><br><span class="line">	r.POST(<span class="string">"/user/search"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="comment">// DefaultPostForm取不到值时会返回指定的默认值</span></span><br><span class="line">		<span class="comment">//username := c.DefaultPostForm("username", "小王子")</span></span><br><span class="line">		username := c.PostForm(<span class="string">"username"</span>)</span><br><span class="line">		address := c.PostForm(<span class="string">"address"</span>)</span><br><span class="line">		<span class="comment">//输出json结果给调用方</span></span><br><span class="line">		c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>:  <span class="string">"ok"</span>,</span><br><span class="line">			<span class="string">"username"</span>: username,</span><br><span class="line">			<span class="string">"address"</span>:  address,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line">	r.Run(<span class="string">":8080"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="获取path参数"><a href="#获取path参数" class="headerlink" title="获取path参数"></a>获取path参数</h3><p>请求的参数通过URL路径传递，例如：<code>/user/search/小王子/沙河</code>。 获取请求URL路径中的参数的方式如下。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">//Default返回一个默认的路由引擎</span></span><br><span class="line">	r := gin.Default()</span><br><span class="line">	r.GET(<span class="string">"/user/search/:username/:address"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		username := c.Param(<span class="string">"username"</span>)</span><br><span class="line">		address := c.Param(<span class="string">"address"</span>)</span><br><span class="line">		<span class="comment">//输出json结果给调用方</span></span><br><span class="line">		c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>:  <span class="string">"ok"</span>,</span><br><span class="line">			<span class="string">"username"</span>: username,</span><br><span class="line">			<span class="string">"address"</span>:  address,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	r.Run(<span class="string">":8080"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="参数绑定"><a href="#参数绑定" class="headerlink" title="参数绑定"></a>参数绑定</h3><p>为了能够更方便的获取请求相关参数，提高开发效率，我们可以基于请求的<code>Content-Type</code>识别请求数据类型并利用反射机制自动提取请求中<code>QueryString</code>、<code>form表单</code>、<code>JSON</code>、<code>XML</code>等参数到结构体中。 下面的示例代码演示了<code>.ShouldBind()</code>强大的功能，它能够基于请求自动提取<code>JSON</code>、<code>form表单</code>和<code>QueryString</code>类型的数据，并把值绑定到指定的结构体对象。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Binding from JSON</span></span><br><span class="line"><span class="keyword">type</span> Login <span class="keyword">struct</span> &#123;</span><br><span class="line">	User     <span class="keyword">string</span> <span class="string">`form:"user" json:"user" binding:"required"`</span></span><br><span class="line">	Password <span class="keyword">string</span> <span class="string">`form:"password" json:"password" binding:"required"`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	router := gin.Default()</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 绑定JSON的示例 (&#123;"user": "q1mi", "password": "123456"&#125;)</span></span><br><span class="line">	router.POST(<span class="string">"/loginJSON"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="keyword">var</span> login Login</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> err := c.ShouldBind(&amp;login); err == <span class="literal">nil</span> &#123;</span><br><span class="line">			fmt.Printf(<span class="string">"login info:%#v\n"</span>, login)</span><br><span class="line">			c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">				<span class="string">"user"</span>:     login.User,</span><br><span class="line">				<span class="string">"password"</span>: login.Password,</span><br><span class="line">			&#125;)</span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			c.JSON(http.StatusBadRequest, gin.H&#123;<span class="string">"error"</span>: err.Error()&#125;)</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 绑定form表单示例 (user=q1mi&amp;password=123456)</span></span><br><span class="line">	router.POST(<span class="string">"/loginForm"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="keyword">var</span> login Login</span><br><span class="line">		<span class="comment">// ShouldBind()会根据请求的Content-Type自行选择绑定器</span></span><br><span class="line">		<span class="keyword">if</span> err := c.ShouldBind(&amp;login); err == <span class="literal">nil</span> &#123;</span><br><span class="line">			c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">				<span class="string">"user"</span>:     login.User,</span><br><span class="line">				<span class="string">"password"</span>: login.Password,</span><br><span class="line">			&#125;)</span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			c.JSON(http.StatusBadRequest, gin.H&#123;<span class="string">"error"</span>: err.Error()&#125;)</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 绑定QueryString示例 (/loginQuery?user=q1mi&amp;password=123456)</span></span><br><span class="line">	router.GET(<span class="string">"/loginForm"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="keyword">var</span> login Login</span><br><span class="line">		<span class="comment">// ShouldBind()会根据请求的Content-Type自行选择绑定器</span></span><br><span class="line">		<span class="keyword">if</span> err := c.ShouldBind(&amp;login); err == <span class="literal">nil</span> &#123;</span><br><span class="line">			c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">				<span class="string">"user"</span>:     login.User,</span><br><span class="line">				<span class="string">"password"</span>: login.Password,</span><br><span class="line">			&#125;)</span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			c.JSON(http.StatusBadRequest, gin.H&#123;<span class="string">"error"</span>: err.Error()&#125;)</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// Listen and serve on 0.0.0.0:8080</span></span><br><span class="line">	router.Run(<span class="string">":8080"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>ShouldBind</code>会按照下面的顺序解析请求中的数据完成绑定：</p>
<ol>
<li>如果是 <code>GET</code> 请求，只使用 <code>Form</code> 绑定引擎（<code>query</code>）。</li>
<li>如果是 <code>POST</code> 请求，首先检查 <code>content-type</code> 是否为 <code>JSON</code> 或 <code>XML</code>，然后再使用 <code>Form</code>（<code>form-data</code>）。</li>
</ol>
<h2 id="文件上传"><a href="#文件上传" class="headerlink" title="文件上传"></a>文件上传</h2><h3 id="单个文件上传"><a href="#单个文件上传" class="headerlink" title="单个文件上传"></a>单个文件上传</h3><p>文件上传前端页面代码：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"zh-CN"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>上传文件示例<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">"/upload"</span> <span class="attr">method</span>=<span class="string">"post"</span> <span class="attr">enctype</span>=<span class="string">"multipart/form-data"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"file"</span> <span class="attr">name</span>=<span class="string">"f1"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> <span class="attr">value</span>=<span class="string">"上传"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>后端gin框架部分代码：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	router := gin.Default()</span><br><span class="line">	<span class="comment">// 处理multipart forms提交文件时默认的内存限制是32 MiB</span></span><br><span class="line">	<span class="comment">// 可以通过下面的方式修改</span></span><br><span class="line">	<span class="comment">// router.MaxMultipartMemory = 8 &lt;&lt; 20  // 8 MiB</span></span><br><span class="line">	router.POST(<span class="string">"/upload"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="comment">// 单个文件</span></span><br><span class="line">		file, err := c.FormFile(<span class="string">"f1"</span>)</span><br><span class="line">		<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">			c.JSON(http.StatusInternalServerError, gin.H&#123;</span><br><span class="line">				<span class="string">"message"</span>: err.Error(),</span><br><span class="line">			&#125;)</span><br><span class="line">			<span class="keyword">return</span></span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		log.Println(file.Filename)</span><br><span class="line">		dst := fmt.Sprintf(<span class="string">"C:/tmp/%s"</span>, file.Filename)</span><br><span class="line">		<span class="comment">// 上传文件到指定的目录</span></span><br><span class="line">		c.SaveUploadedFile(file, dst)</span><br><span class="line">		c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>: fmt.Sprintf(<span class="string">"'%s' uploaded!"</span>, file.Filename),</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line">	router.Run()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="多个文件上传"><a href="#多个文件上传" class="headerlink" title="多个文件上传"></a>多个文件上传</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	router := gin.Default()</span><br><span class="line">	<span class="comment">// 处理multipart forms提交文件时默认的内存限制是32 MiB</span></span><br><span class="line">	<span class="comment">// 可以通过下面的方式修改</span></span><br><span class="line">	<span class="comment">// router.MaxMultipartMemory = 8 &lt;&lt; 20  // 8 MiB</span></span><br><span class="line">	router.POST(<span class="string">"/upload"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		<span class="comment">// Multipart form</span></span><br><span class="line">		form, _ := c.MultipartForm()</span><br><span class="line">		files := form.File[<span class="string">"file"</span>]</span><br><span class="line"></span><br><span class="line">		<span class="keyword">for</span> index, file := <span class="keyword">range</span> files &#123;</span><br><span class="line">			log.Println(file.Filename)</span><br><span class="line">			dst := fmt.Sprintf(<span class="string">"C:/tmp/%s_%d"</span>, file.Filename, index)</span><br><span class="line">			<span class="comment">// 上传文件到指定的目录</span></span><br><span class="line">			c.SaveUploadedFile(file, dst)</span><br><span class="line">		&#125;</span><br><span class="line">		c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>: fmt.Sprintf(<span class="string">"%d files uploaded!"</span>, <span class="built_in">len</span>(files)),</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line">	router.Run()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="重定向"><a href="#重定向" class="headerlink" title="重定向"></a>重定向</h2><h3 id="HTTP重定向"><a href="#HTTP重定向" class="headerlink" title="HTTP重定向"></a>HTTP重定向</h3><p>HTTP 重定向很容易。 内部、外部重定向均支持。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">r.GET(<span class="string">"/test"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">	c.Redirect(http.StatusMovedPermanently, <span class="string">"http://www.sogo.com/"</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="路由重定向"><a href="#路由重定向" class="headerlink" title="路由重定向"></a>路由重定向</h3><p>路由重定向，使用<code>HandleContext</code>：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">r.GET(<span class="string">"/test"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">    <span class="comment">// 指定重定向的URL</span></span><br><span class="line">    c.Request.URL.Path = <span class="string">"/test2"</span></span><br><span class="line">    r.HandleContext(c)</span><br><span class="line">&#125;)</span><br><span class="line">r.GET(<span class="string">"/test2"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">    c.JSON(http.StatusOK, gin.H&#123;<span class="string">"hello"</span>: <span class="string">"world"</span>&#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h2 id="Gin路由"><a href="#Gin路由" class="headerlink" title="Gin路由"></a>Gin路由</h2><h3 id="普通路由"><a href="#普通路由" class="headerlink" title="普通路由"></a>普通路由</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">r.GET(<span class="string">"/index"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">r.GET(<span class="string">"/login"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">r.POST(<span class="string">"/login"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br></pre></td></tr></table></figure>

<p>此外，还有一个可以匹配所有请求方法的<code>Any</code>方法如下：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">r.Any(<span class="string">"/test"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br></pre></td></tr></table></figure>

<p>为没有配置处理函数的路由添加处理程序，默认情况下它返回404代码，下面的代码为没有匹配到路由的请求都返回<code>views/404.html</code>页面。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">r.NoRoute(<span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.HTML(http.StatusNotFound, <span class="string">"views/404.html"</span>, <span class="literal">nil</span>)</span><br><span class="line">	&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="路由组"><a href="#路由组" class="headerlink" title="路由组"></a>路由组</h3><p>我们可以将拥有共同URL前缀的路由划分为一个路由组。习惯性一对<code>{}</code>包裹同组的路由，这只是为了看着清晰，你用不用<code>{}</code>包裹功能上没什么区别。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	r := gin.Default()</span><br><span class="line">	userGroup := r.Group(<span class="string">"/user"</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		userGroup.GET(<span class="string">"/index"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">		userGroup.GET(<span class="string">"/login"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">		userGroup.POST(<span class="string">"/login"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">	shopGroup := r.Group(<span class="string">"/shop"</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		shopGroup.GET(<span class="string">"/index"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">		shopGroup.GET(<span class="string">"/cart"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">		shopGroup.POST(<span class="string">"/checkout"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">	&#125;</span><br><span class="line">	r.Run()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>路由组也是支持嵌套的，例如：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">shopGroup := r.Group(<span class="string">"/shop"</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		shopGroup.GET(<span class="string">"/index"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">		shopGroup.GET(<span class="string">"/cart"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">		shopGroup.POST(<span class="string">"/checkout"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">		<span class="comment">// 嵌套路由组</span></span><br><span class="line">		xx := shopGroup.Group(<span class="string">"xx"</span>)</span><br><span class="line">		xx.GET(<span class="string">"/oo"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>通常我们将路由分组用在划分业务逻辑或划分API版本时。</p>
<h3 id="路由原理"><a href="#路由原理" class="headerlink" title="路由原理"></a>路由原理</h3><p>Gin框架中的路由使用的是<a href="https://github.com/julienschmidt/httprouter" target="_blank" rel="noopener">httprouter</a>这个库。</p>
<p>其基本原理就是构造一个路由地址的前缀树。</p>
<h2 id="Gin中间件"><a href="#Gin中间件" class="headerlink" title="Gin中间件"></a>Gin中间件</h2><p>Gin框架允许开发者在处理请求的过程中，加入用户自己的钩子（Hook）函数。这个钩子函数就叫中间件，中间件适合处理一些公共的业务逻辑，比如登录认证、权限校验、数据分页、记录日志、耗时统计等。</p>
<h3 id="定义中间件"><a href="#定义中间件" class="headerlink" title="定义中间件"></a>定义中间件</h3><p>Gin中的中间件必须是一个<code>gin.HandlerFunc</code>类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// StatCost 是一个统计耗时请求耗时的中间件</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">StatCost</span><span class="params">()</span> <span class="title">gin</span>.<span class="title">HandlerFunc</span></span> &#123;</span><br><span class="line">	<span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		start := time.Now()</span><br><span class="line">		c.Set(<span class="string">"name"</span>, <span class="string">"小王子"</span>) <span class="comment">// 可以通过c.Set在请求上下文中设置值，后续的处理函数能够取到该值</span></span><br><span class="line">		<span class="comment">// 调用该请求的剩余处理程序</span></span><br><span class="line">		c.Next()</span><br><span class="line">		<span class="comment">// 不调用该请求的剩余处理程序</span></span><br><span class="line">		<span class="comment">// c.Abort()</span></span><br><span class="line">		<span class="comment">// 计算耗时</span></span><br><span class="line">		cost := time.Since(start)</span><br><span class="line">		log.Println(cost)</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="注册中间件"><a href="#注册中间件" class="headerlink" title="注册中间件"></a>注册中间件</h3><p>在gin框架中，我们可以为每个路由添加任意数量的中间件。</p>
<h4 id="为全局路由注册"><a href="#为全局路由注册" class="headerlink" title="为全局路由注册"></a>为全局路由注册</h4><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">// 新建一个没有任何默认中间件的路由</span></span><br><span class="line">	r := gin.New()</span><br><span class="line">	<span class="comment">// 注册一个全局中间件</span></span><br><span class="line">	r.Use(StatCost())</span><br><span class="line">	</span><br><span class="line">	r.GET(<span class="string">"/test"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		name := c.MustGet(<span class="string">"name"</span>).(<span class="keyword">string</span>) <span class="comment">// 从上下文取值</span></span><br><span class="line">		log.Println(name)</span><br><span class="line">		c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>: <span class="string">"Hello world!"</span>,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br><span class="line">	r.Run()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="为某个路由单独注册"><a href="#为某个路由单独注册" class="headerlink" title="为某个路由单独注册"></a>为某个路由单独注册</h4><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 给/test2路由单独注册中间件（可注册多个）</span></span><br><span class="line">	r.GET(<span class="string">"/test2"</span>, StatCost(), <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		name := c.MustGet(<span class="string">"name"</span>).(<span class="keyword">string</span>) <span class="comment">// 从上下文取值</span></span><br><span class="line">		log.Println(name)</span><br><span class="line">		c.JSON(http.StatusOK, gin.H&#123;</span><br><span class="line">			<span class="string">"message"</span>: <span class="string">"Hello world!"</span>,</span><br><span class="line">		&#125;)</span><br><span class="line">	&#125;)</span><br></pre></td></tr></table></figure>

<h4 id="为路由组注册中间件"><a href="#为路由组注册中间件" class="headerlink" title="为路由组注册中间件"></a>为路由组注册中间件</h4><p>为路由组注册中间件有以下两种写法。</p>
<p>写法1：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">shopGroup := r.Group(<span class="string">"/shop"</span>, StatCost())</span><br><span class="line">&#123;</span><br><span class="line">    shopGroup.GET(<span class="string">"/index"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>写法2：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">shopGroup := r.Group(<span class="string">"/shop"</span>)</span><br><span class="line">shopGroup.Use(StatCost())</span><br><span class="line">&#123;</span><br><span class="line">    shopGroup.GET(<span class="string">"/index"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;...&#125;)</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="中间件注意事项"><a href="#中间件注意事项" class="headerlink" title="中间件注意事项"></a>中间件注意事项</h3><h4 id="gin默认中间件"><a href="#gin默认中间件" class="headerlink" title="gin默认中间件"></a>gin默认中间件</h4><p><code>gin.Default()</code>默认使用了<code>Logger</code>和<code>Recovery</code>中间件，其中：</p>
<ul>
<li><code>Logger</code>中间件将日志写入<code>gin.DefaultWriter</code>，即使配置了<code>GIN_MODE=release</code>。</li>
<li><code>Recovery</code>中间件会recover任何<code>panic</code>。如果有panic的话，会写入500响应码。</li>
</ul>
<p>如果不想使用上面两个默认的中间件，可以使用<code>gin.New()</code>新建一个没有任何默认中间件的路由。</p>
<h4 id="gin中间件中使用goroutine"><a href="#gin中间件中使用goroutine" class="headerlink" title="gin中间件中使用goroutine"></a>gin中间件中使用goroutine</h4><p>当在中间件或<code>handler</code>中启动新的<code>goroutine</code>时，<strong>不能使用</strong>原始的上下文（c *gin.Context），必须使用其只读副本（<code>c.Copy()</code>）。</p>
<h2 id="运行多个服务"><a href="#运行多个服务" class="headerlink" title="运行多个服务"></a>运行多个服务</h2><p>我们可以在多个端口启动服务，例如：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	<span class="string">"log"</span></span><br><span class="line">	<span class="string">"net/http"</span></span><br><span class="line">	<span class="string">"time"</span></span><br><span class="line"></span><br><span class="line">	<span class="string">"github.com/gin-gonic/gin"</span></span><br><span class="line">	<span class="string">"golang.org/x/sync/errgroup"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	g errgroup.Group</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">router01</span><span class="params">()</span> <span class="title">http</span>.<span class="title">Handler</span></span> &#123;</span><br><span class="line">	e := gin.New()</span><br><span class="line">	e.Use(gin.Recovery())</span><br><span class="line">	e.GET(<span class="string">"/"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.JSON(</span><br><span class="line">			http.StatusOK,</span><br><span class="line">			gin.H&#123;</span><br><span class="line">				<span class="string">"code"</span>:  http.StatusOK,</span><br><span class="line">				<span class="string">"error"</span>: <span class="string">"Welcome server 01"</span>,</span><br><span class="line">			&#125;,</span><br><span class="line">		)</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> e</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">router02</span><span class="params">()</span> <span class="title">http</span>.<span class="title">Handler</span></span> &#123;</span><br><span class="line">	e := gin.New()</span><br><span class="line">	e.Use(gin.Recovery())</span><br><span class="line">	e.GET(<span class="string">"/"</span>, <span class="function"><span class="keyword">func</span><span class="params">(c *gin.Context)</span></span> &#123;</span><br><span class="line">		c.JSON(</span><br><span class="line">			http.StatusOK,</span><br><span class="line">			gin.H&#123;</span><br><span class="line">				<span class="string">"code"</span>:  http.StatusOK,</span><br><span class="line">				<span class="string">"error"</span>: <span class="string">"Welcome server 02"</span>,</span><br><span class="line">			&#125;,</span><br><span class="line">		)</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> e</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	server01 := &amp;http.Server&#123;</span><br><span class="line">		Addr:         <span class="string">":8080"</span>,</span><br><span class="line">		Handler:      router01(),</span><br><span class="line">		ReadTimeout:  <span class="number">5</span> * time.Second,</span><br><span class="line">		WriteTimeout: <span class="number">10</span> * time.Second,</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	server02 := &amp;http.Server&#123;</span><br><span class="line">		Addr:         <span class="string">":8081"</span>,</span><br><span class="line">		Handler:      router02(),</span><br><span class="line">		ReadTimeout:  <span class="number">5</span> * time.Second,</span><br><span class="line">		WriteTimeout: <span class="number">10</span> * time.Second,</span><br><span class="line">	&#125;</span><br><span class="line">   <span class="comment">// 借助errgroup.Group或者自行开启两个goroutine分别启动两个服务</span></span><br><span class="line">	g.Go(<span class="function"><span class="keyword">func</span><span class="params">()</span> <span class="title">error</span></span> &#123;</span><br><span class="line">		<span class="keyword">return</span> server01.ListenAndServe()</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	g.Go(<span class="function"><span class="keyword">func</span><span class="params">()</span> <span class="title">error</span></span> &#123;</span><br><span class="line">		<span class="keyword">return</span> server02.ListenAndServe()</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> err := g.Wait(); err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatal(err)</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<hr>

    </div>

  </article>
  <div class="toc-container">
    
  <div id="toc" class="toc-article">
    <strong class="toc-title">目录</strong>
    <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#Gin框架介绍"><span class="toc-text">Gin框架介绍</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Gin框架安装与使用"><span class="toc-text">Gin框架安装与使用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#安装"><span class="toc-text">安装</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#第一个Gin示例："><span class="toc-text">第一个Gin示例：</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#RESTful-API"><span class="toc-text">RESTful API</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Gin渲染"><span class="toc-text">Gin渲染</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#HTML渲染"><span class="toc-text">HTML渲染</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#自定义模板函数"><span class="toc-text">自定义模板函数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#静态文件处理"><span class="toc-text">静态文件处理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用模板继承"><span class="toc-text">使用模板继承</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#补充文件路径处理"><span class="toc-text">补充文件路径处理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#JSON渲染"><span class="toc-text">JSON渲染</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#XML渲染"><span class="toc-text">XML渲染</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#YMAL渲染"><span class="toc-text">YMAL渲染</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#protobuf渲染"><span class="toc-text">protobuf渲染</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#获取参数"><span class="toc-text">获取参数</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#获取querystring参数"><span class="toc-text">获取querystring参数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#获取form参数"><span class="toc-text">获取form参数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#获取path参数"><span class="toc-text">获取path参数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#参数绑定"><span class="toc-text">参数绑定</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#文件上传"><span class="toc-text">文件上传</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#单个文件上传"><span class="toc-text">单个文件上传</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#多个文件上传"><span class="toc-text">多个文件上传</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#重定向"><span class="toc-text">重定向</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#HTTP重定向"><span class="toc-text">HTTP重定向</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#路由重定向"><span class="toc-text">路由重定向</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Gin路由"><span class="toc-text">Gin路由</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#普通路由"><span class="toc-text">普通路由</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#路由组"><span class="toc-text">路由组</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#路由原理"><span class="toc-text">路由原理</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Gin中间件"><span class="toc-text">Gin中间件</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#定义中间件"><span class="toc-text">定义中间件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#注册中间件"><span class="toc-text">注册中间件</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#为全局路由注册"><span class="toc-text">为全局路由注册</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#为某个路由单独注册"><span class="toc-text">为某个路由单独注册</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#为路由组注册中间件"><span class="toc-text">为路由组注册中间件</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#中间件注意事项"><span class="toc-text">中间件注意事项</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#gin默认中间件"><span class="toc-text">gin默认中间件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#gin中间件中使用goroutine"><span class="toc-text">gin中间件中使用goroutine</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#运行多个服务"><span class="toc-text">运行多个服务</span></a></li></ol>
  </div>


  </div>
</div>
<script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=5sr5du46f27&amp;m=0&amp;c=ff0000&amp;cr1=ffffff&amp;f=arial&amp;l=33" async="async"></script>
<div class="copyright">
    <span>本作品采用</span>
    <a href="https://creativecommons.org/licenses/by/4.0/">知识共享署名 4.0 国际许可协议</a>
    <span>进行许可。 转载时请注明原文链接。</span>
</div>


  
    <div class="post-nav" style="margin-left:-168px;">
      <div class="post-nav-item post-nav-next">
        
          <span>〈 </span>
          <a href="/2020/04/26/第一章Python的介绍和安装/" rel="next" title="第一章 Python的简介和安装">
          第一章 Python的简介和安装
          </a>
        
      </div>
  
      <div class="post-nav-item post-nav-prev">
          
          <a href="/2020/06/07/Jwt的介绍和使用/" rel="prev" title="Jwt的介绍">
            Jwt的介绍
          </a>
          <span>〉</span>
        
      </div>
    </div>
  


	
	<div style="width:109%; margin-left:-144px" id="lv-container" data-id="city" data-uid="MTAyMC80OTg5NS8yNjM4Ng==">
	<script type="text/javascript">
   	   (function(d, s) {
       		var j, e = d.getElementsByTagName(s)[0];

       		if (typeof LivereTower === 'function') { return; }

       		j = d.createElement(s);
       		j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
       		j.async = true;

       		e.parentNode.insertBefore(j, e);
   	   })(document, 'script');
	</script>
	<noscript> 为正常使用来必力评论功能请激活JavaScript</noscript>
        </div>
	



    </div>

    

  </div>
  <footer class="footer text-center">
    <div id="bottom-inner">
        <a class="bottom-item" href target="_blank">GitHub</a> |
        <a class="bottom-item" href>友情链接</a> |
        <a class="bottom-item" href="https://hexo.io" target="_blank">Powered by hexo</a> |
        <a class="bottom-item" href="https://github.com/fooying/hexo-theme-xoxo-plus" target="_blank">Theme xoxo-plus</a> |
        <a class="bottom-item" href="/atom.xml">订阅</a>
    </div>
</footer>

  

<script>
  (function(window, document, undefined) {

    var timer = null;

    function returnTop() {
      cancelAnimationFrame(timer);
      timer = requestAnimationFrame(function fn() {
        var oTop = document.body.scrollTop || document.documentElement.scrollTop;
        if (oTop > 0) {
          document.body.scrollTop = document.documentElement.scrollTop = oTop - 50;
          timer = requestAnimationFrame(fn);
        } else {
          cancelAnimationFrame(timer);
        }
      });
    }

    var hearts = [];
    window.requestAnimationFrame = (function() {
      return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(callback) {
          setTimeout(callback, 1000 / 60);
        }
    })();
    init();

    function init() {
      css(".heart{z-index:9999;width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: absolute;}.heart:after{top: -5px;}.heart:before{left: -5px;}");
      attachEvent();
      gameloop();
      addMenuEvent();
    }

    function gameloop() {
      for (var i = 0; i < hearts.length; i++) {
        if (hearts[i].alpha <= 0) {
          document.body.removeChild(hearts[i].el);
          hearts.splice(i, 1);
          continue;
        }
        hearts[i].y--;
        hearts[i].scale += 0.004;
        hearts[i].alpha -= 0.013;
        hearts[i].el.style.cssText = "left:" + hearts[i].x + "px;top:" + hearts[i].y + "px;opacity:" + hearts[i].alpha + ";transform:scale(" + hearts[i].scale + "," + hearts[i].scale + ") rotate(45deg);background:" + hearts[i].color;
      }
      requestAnimationFrame(gameloop);
    }

    /**
     * 给logo设置点击事件
     * 
     * - 回到顶部
     * - 出现爱心
     */
    function attachEvent() {
      var old = typeof window.onclick === "function" && window.onclick;
      var logo = document.getElementById("logo");
      if (logo) {
        logo.onclick = function(event) {
          returnTop();
          old && old();
          createHeart(event);
        }
      }
      
    }

    function createHeart(event) {
      var d = document.createElement("div");
      d.className = "heart";
      hearts.push({
        el: d,
        x: event.clientX - 5,
        y: event.clientY - 5,
        scale: 1,
        alpha: 1,
        color: randomColor()
      });
      document.body.appendChild(d);
    }

    function css(css) {
      var style = document.createElement("style");
      style.type = "text/css";
      try {
        style.appendChild(document.createTextNode(css));
      } catch (ex) {
        style.styleSheet.cssText = css;
      }
      document.getElementsByTagName('head')[0].appendChild(style);
    }

    function randomColor() {
      // return "rgb(" + (~~(Math.random() * 255)) + "," + (~~(Math.random() * 255)) + "," + (~~(Math.random() * 255)) + ")";
      return "#F44336";
    }

    function addMenuEvent() {
      var menu = document.getElementById('menu-main-post');
      if (menu) {
        var toc = document.getElementById('toc');
        if (toc) {
          menu.onclick = function() {
            if (toc) {
              if (toc.style.display == 'block') {
                toc.style.display = 'none';
              } else {
                toc.style.display = 'block';
              }
            }
          };
        } else {
          menu.style.display = 'none';
        }
      }
    }

  })(window, document);
</script>

  



  

</body>
</html>
